This analysis evaluates chromosomal instability by formatting SV and CNV data into chromosomal breaks and measuring chromosomal break density. This notebook returns chromosomal break heatmaps and plots for each tumor type group in the plots directory.

Code adapted from svcnvplus.

Usage

This notebook can be run via the command line from the top directory of the repository as follows:

Rscript -e "rmarkdown::render('analyses/chromosomal-instability/chromosomal-instability.Rmd', 
                              clean = TRUE)"

Set Up

# Set seed so heatmaps turn out the same
set.seed(2020)

# This threshold will be used to determine percent of genome changed
ch.pct <- 0.02

# Magrittr pipe
`%>%` <- dplyr::`%>%`

Read in the custom functions needed for this analysis.

source(file.path("util", "chr-break-calculate.R"))
source(file.path("util", "chr-break-plot.R"))

Directories and Files

# Path to data directory
data_dir <- file.path("..", "..", "data")

# Path to output directory
plots_dir <- "plots"

# Path to tumor type plots output directory
hist_plots_dir <- file.path(plots_dir, "tumor-type")

# Create the hist_plots_dir  if it does not exist
if (!dir.exists(hist_plots_dir)) {
  dir.create(hist_plots_dir, recursive = TRUE)
}

Read in data and format it

Set up metadata

# Read in the metadata
metadata <- readr::read_tsv(file.path(data_dir, "pbta-histologies.tsv"))
Parsed with column specification:
cols(
  .default = col_character(),
  OS_days = col_double(),
  age_last_update_days = col_double(),
  normal_fraction = col_double(),
  tumor_fraction = col_double(),
  tumor_ploidy = col_double()
)
See spec(...) for full column specifications.

Set up CNV data:

Read in the CNV data and format it to make the chromosome and coordinates easier for handling later.

# Read in the segment copy number data
cnv_df <- data.table::fread(file.path(data_dir, "pbta-cnv-cnvkit.seg.gz"),
  data.table = FALSE
) %>%
  dplyr::mutate(
    samples = as.factor(ID),
    # Calculate width
    width = loc.end - loc.start,
    # Make a binary variable that labels whether or not a sequence
    # is considered changed by the threshold set
    changed = as.factor(dplyr::case_when(
      seg.mean < log2(1 - ch.pct) ~ "change",
      seg.mean >= log2(1 + ch.pct) ~ "change", 
      TRUE ~ "no_change"
    ))
  ) %>%
  # Reformat the chromosome variable to drop the "chr"
  dplyr::mutate(chrom = factor(gsub("chr", "", chrom),
    levels = c(1:22, "X", "Y")
  )) %>%
  # Changing these so they end up matching the SV data
  dplyr::rename(start = loc.start, end = loc.end)

Filter CNV data to only the changes that are larger than our cutoff ch.pct.

cnv_filtered_df <- cnv_df %>%
  dplyr::filter(changed == "change")

Set up SV data

Read in the SV data.

sv_df <- data.table::fread(file.path(data_dir, "pbta-sv-manta.tsv.gz"), 
                           data.table = FALSE) %>%
  # Reformat the 23 and 24 chromosomes so they are X and Y and also factors
  dplyr::mutate(
    chrom = dplyr::recode(SV.chrom,
      `23` = "X", `24` = "Y"
    )
  )

Set up chromosome reference data

Set up chromosome size data. It just so happens that this BED file: WGS.hg38.strelka2.unpadded.bed is actually just a list of the chromosome sizes so we are using that for now.

# Set up Chr sizes
chr_sizes <- readr::read_tsv(file.path(data_dir, "WGS.hg38.strelka2.unpadded.bed"),
  col_names = FALSE
) %>%
  # Reformat the chromosome variable to drop the "chr"
  dplyr::mutate(X1 = factor(gsub("chr", "", X1),
    levels = c(1:22, "X", "Y", "M")
  )) %>%
  # Remove sex chromosomes
  dplyr::filter(!(X1 %in% c("X", "Y", "M")))
Parsed with column specification:
cols(
  X1 = col_character(),
  X2 = col_double(),
  X3 = col_double()
)
# Make chromosome size named vector
chr_sizes_vector <- chr_sizes$X3
names(chr_sizes_vector) <- chr_sizes$X1

Format the data as chromosomes breaks

The SV and CNV data comes to us in the form of ranges, but for getting a look at chromosomal instability, we will want to convert this data into single break points of the genome. We’ll also remove the sex chromosomes.

Make an CNV breaks data.frame.

# Make a CNV data.frame that has the breaks
cnv_breaks <- data.frame(
  samples = rep(cnv_filtered_df$ID, 2),
  chrom = rep(cnv_filtered_df$chrom, 2),
  start = c(cnv_filtered_df$start, cnv_filtered_df$end),
  end = c(cnv_filtered_df$start, cnv_filtered_df$end),
  seg.mean = rep(cnv_filtered_df$seg.mean, 2),
  copy.num = rep(cnv_filtered_df$copy.num, 2),
  stringsAsFactors = FALSE
) %>%
  # Remove sex chromosomes
  dplyr::filter(!(chrom %in% c("X", "Y")))

Make an SV breaks data.frame.

# Make a data.frame that has the breaks
sv_breaks <- data.frame(
  samples = rep(sv_df$Kids.First.Biospecimen.ID.Tumor, 2),
  chrom = rep(sv_df$chrom, 2),
  start = c(sv_df$SV.start, sv_df$SV.end),
  end = c(sv_df$SV.start, sv_df$SV.end),
  svclass = rep(sv_df$SV.type, 2),
  stringsAsFactors = FALSE
) %>%
  # Remove sex chromosomes and NAs
  dplyr::filter(!(chrom %in% c("X", "Y", "M", NA)))

Co-localization of breakpoints for samples

For each sample, we will bin the genome and count how many chromosome breaks occur for each bin, given the given bin_size.

We will run this for each sample and return a list of three GenomicRanges objects: 1) common_density contains the combined break counts for both SV and CNV break data.
2) cnv_density contains the number of break counts for CNV.
3) sv_density contains the number of break counts for SV.

# Change here and it will change the rest
bin_size <- 1e6

# Obtain a list of samples that are common to both datasets. 
common_samples <- intersect(sv_breaks$samples, cnv_breaks$samples)

# Get a big list of break densities for each sample.
sample_densities <- lapply(common_samples, function(sample_id) {
  chr_break_list(
    cnv_breaks = cnv_breaks, 
    sv_breaks = sv_breaks, 
    sample_id = sample_id, 
    chr_sizes_vector = chr_sizes_vector)
})

# Bring along the sample IDs
names(sample_densities) <- common_samples

Set up for making heatmaps of the breakpoints

Given the GenomicRanges objects for each sample, create a combined plot for each.

Make chromosome labeling HeatmapAnnotation object.

# Extract chromosome names from a GenomicRanges object
chr_labels <- sample_densities[[1]]$common_density@seqnames
chr_labels <- paste0("chr", as.factor(chr_labels))

# Make a key for assigning alternating colors to the chromosomes
chr_colors <- rep(c("grey", "lightblue"), length.out = length(unique(chr_labels)))
names(chr_colors) <- unique(chr_labels) 

# Create the Heatmap annotation object
chr_annot <- ComplexHeatmap::HeatmapAnnotation(df = data.frame(chr_labels), col = list(chr_labels = c(chr_colors)), 
                                               name ="",
                                               show_legend = FALSE, 
                                               show_annotation_name = FALSE)

Make histology labeling HeatmapAnnotation object.

# Get the histologies for the samples in this set and order them by histology
histologies <- 
  data.frame(Kids_First_Biospecimen_ID = common_samples) %>%
  dplyr::inner_join(dplyr::select(metadata, Kids_First_Biospecimen_ID, short_histology)) %>% 
  dplyr::mutate(short_histology = as.factor(short_histology)) %>% 
  dplyr::arrange(short_histology) %>% 
  tibble::column_to_rownames("Kids_First_Biospecimen_ID") 
Joining, by = "Kids_First_Biospecimen_ID"
Column `Kids_First_Biospecimen_ID` joining factor and character vector, coercing into character vector
# Create the Heatmap annotation object
hist_annot <- ComplexHeatmap::rowAnnotation(df = histologies,
                                            show_annotation_name = FALSE)

Make a color function.

col_fun <- circlize::colorRamp2(c(0, 2, 5, 10, 20),
                                c("#edf8fb", "#b2e2e2", "#66c2a4", "#2ca25f", "#006d2c"))

Make a function for making the heatmaps.

breaks_heatmap <- function(data_name) {
  # A wrapper function for making a heatmap from the samples GenomicRanges list. 
  # 
  # Args:
  # data_name: a character string that matches a name in the list. 

  # Returns: 
  # A heatmap of the chromosomal breaks

  # Pull out the total_counts 
  total_counts <- lapply(sample_densities, function(granges_list) {
    granges_list[[data_name]]@elementMetadata@listData$total_counts
  })
  # Make into a data.frame
  total_counts <- dplyr::bind_rows(total_counts) %>%
    dplyr::select(rownames(histologies)) %>% 
    t()
  # Add chromosome labels
  colnames(total_counts) <- chr_labels
  # Plot on a heatmap
  heatmap <- ComplexHeatmap::Heatmap(total_counts, 
                                            col = col_fun, 
                                            heatmap_legend_param = list(title = "Count of chr breaks"),
                                            cluster_columns = FALSE,
                                            cluster_rows = FALSE,
                                            show_column_names = FALSE, 
                                            show_row_names = FALSE, 
                                            bottom_annotation = chr_annot, 
                                            left_annotation = hist_annot)
  # Return plot
  return(heatmap)
}

Common breaks heatmap

common_heatmap <- breaks_heatmap(data_name = "common_density")

# Save plot as PNG
png(file.path(plots_dir, paste0("common_density_heatmap.png")), 
  width = 1200, height = 900, units = "px")
  common_heatmap
dev.off()
null device 
          1 
# Print out here
common_heatmap

CNV breaks heatmap

cnv_heatmap <- breaks_heatmap(data_name = "cnv_density")

# Save plot as PNG
png(file.path(plots_dir, paste0("cnv_density_heatmap.png")), 
  width = 1200, height = 900, units = "px")
  cnv_heatmap
dev.off()
null device 
          1 
# Print out here
cnv_heatmap

SV breaks heatmap

sv_heatmap <- breaks_heatmap(data_name = "sv_density")

# Save plot as PNG
png(file.path(plots_dir, paste0("sv_density_heatmap.png")), 
  width = 1200, height = 900, units = "px")
  sv_heatmap
dev.off()
null device 
          1 
# Print out here
sv_heatmap

Co-localization of breakpoints for tumor-type groups

Same as was done for each sample, now we will calculate densities for each tumor-type group.

# Change bin_size here and it will change the rest
bin_size <- 1e6

# Get a list of the tumor_types for which we have DNA-seq data
tumor_types <- metadata %>% 
  dplyr::filter(!is.na(short_histology), experimental_strategy != "RNA-Seq") %>% 
  dplyr::distinct(short_histology) %>%
  dplyr::pull(short_histology)

Run the density calculations for the groups.

# Get a big list of break densities for each sample.
group_densities <- lapply(tumor_types, function(tumor_type) {

  # Obtain a list of sample_ids to subset by
  sample_ids <- metadata %>%
    dplyr::filter(metadata$short_histology == tumor_type) %>%
    dplyr::pull(Kids_First_Biospecimen_ID)
  
  # Double check these samples are in the list
  check_samples <- (sample_ids %in% common_samples)
  
  # If no samples, go to next
  if (sum(check_samples) > 1) {
  message(paste("Calculating breakpoint density for", tumor_type, "samples"))
  chr_break_list(cnv_breaks = cnv_breaks, 
                 sv_breaks = sv_breaks, 
                 sample_id = sample_ids, 
                 chr_sizes_vector = chr_sizes_vector)
  }
})
Calculating breakpoint density for Ependymoma samples
Calculating breakpoint density for HGAT samples
Calculating breakpoint density for LGAT samples
Calculating breakpoint density for Other samples
Calculating breakpoint density for CNS sarcoma samples
Calculating breakpoint density for Medulloblastoma samples
Calculating breakpoint density for Schwannoma samples
Calculating breakpoint density for ATRT samples
Calculating breakpoint density for Choroid plexus tumor samples
Calculating breakpoint density for Craniopharyngioma samples
Calculating breakpoint density for DNET samples
Calculating breakpoint density for Teratoma samples
Calculating breakpoint density for Hemangioblastoma samples
Calculating breakpoint density for Meningioma samples
Calculating breakpoint density for Adenoma samples
Calculating breakpoint density for Neurofibroma samples
Calculating breakpoint density for Ganglioglioma samples
Calculating breakpoint density for Langerhans Cell histiocytosis samples
Calculating breakpoint density for CNS Embryonal tumor samples
Calculating breakpoint density for CNS neuroblastoma samples
Calculating breakpoint density for Chordoma samples
Calculating breakpoint density for MPNST samples
Calculating breakpoint density for Glial-neuronal tumor NOS samples
Calculating breakpoint density for Pineoblastoma samples
Calculating breakpoint density for CNS EFT-CIC samples
Calculating breakpoint density for Central neurocytoma samples
Calculating breakpoint density for Germinoma samples
Calculating breakpoint density for CNS Rhabdomyosarcoma samples
Calculating breakpoint density for Dysplasia samples
Calculating breakpoint density for Oligodendroglioma samples
Calculating breakpoint density for Hemangioma samples
Calculating breakpoint density for LGMT samples
Calculating breakpoint density for CNS ganglioneuroblastoma samples
# Bring along the tumor-type labels
names(group_densities) <- tumor_types

# Remove tumor_types data that didn't have at least two samples
group_densities <- group_densities[!sapply(group_densities, is.null)]

Plot the breakpoints for each tumor-type

Here we will plot median number of break points for the tumor-type group per each bin.

purrr::imap(group_densities, function(.x, name = .y){
  # Make the combo plot
  multipanel_break_plot(
    granges_list = .x,
    plot_name = name,
    y_val = "median_counts",
    y_lab = "Median Breaks per Mb",
    plot_dir = hist_plots_dir
  )
})
$Ependymoma

$HGAT

$LGAT

$Other

$`CNS sarcoma`

$Medulloblastoma

$Schwannoma

$ATRT

$`Choroid plexus tumor`

$Craniopharyngioma

$DNET

$Teratoma

$Hemangioblastoma

$Meningioma

$Adenoma

$Neurofibroma

$Ganglioglioma

$`Langerhans Cell histiocytosis`

$`CNS Embryonal tumor`

$`CNS neuroblastoma`

$Chordoma

$MPNST

$`Glial-neuronal tumor NOS`

$Pineoblastoma

$`CNS EFT-CIC`

$`Central neurocytoma`

$Germinoma

$`CNS Rhabdomyosarcoma`

$Dysplasia

$Oligodendroglioma

$Hemangioma

$LGMT

$`CNS ganglioneuroblastoma`

Zip up the PNG files into one file.

# Declare name of zip file
zip_file <- paste0(hist_plots_dir, ".zip")

# Remove any current zip_file of this name so we can overwrite it
if (file.exists(zip_file)){
  file.remove(zip_file)
}
[1] TRUE
# Zip up the plots
zip(zip_file, hist_plots_dir)
  adding: plots/tumor-type/ (stored 0%)
  adding: plots/tumor-type/Ependymoma_breaks.png (deflated 16%)
  adding: plots/tumor-type/CNS Embryonal tumor_breaks.png (deflated 17%)
  adding: plots/tumor-type/Glial-neuronal tumor NOS_breaks.png (deflated 15%)
  adding: plots/tumor-type/ATRT_breaks.png (deflated 17%)
  adding: plots/tumor-type/Medulloblastoma_breaks.png (deflated 16%)
  adding: plots/tumor-type/Oligodendroglioma_breaks.png (deflated 11%)
  adding: plots/tumor-type/LGMT_breaks.png (deflated 11%)
  adding: plots/tumor-type/Gliosis_breaks.png (deflated 12%)
  adding: plots/tumor-type/.DS_Store (deflated 97%)
  adding: plots/tumor-type/HGAT_breaks.png (deflated 16%)
  adding: plots/tumor-type/Hemangioma_breaks.png (deflated 11%)
  adding: plots/tumor-type/LGAT_breaks.png (deflated 16%)
  adding: plots/tumor-type/Pineoblastoma_breaks.png (deflated 15%)
  adding: plots/tumor-type/Dysplasia_breaks.png (deflated 17%)
  adding: plots/tumor-type/MPNST_breaks.png (deflated 14%)
  adding: plots/tumor-type/Central neurocytoma_breaks.png (deflated 11%)
  adding: plots/tumor-type/CNS neuroblastoma_breaks.png (deflated 14%)
  adding: plots/tumor-type/Neurofibroma_breaks.png (deflated 17%)
  adding: plots/tumor-type/Chordoma_breaks.png (deflated 18%)
  adding: plots/tumor-type/Schwannoma_breaks.png (deflated 17%)
  adding: plots/tumor-type/Hemangioblastoma_breaks.png (deflated 15%)
  adding: plots/tumor-type/DNET_breaks.png (deflated 17%)
  adding: plots/tumor-type/Langerhans Cell histiocytosis_breaks.png (deflated 15%)
  adding: plots/tumor-type/CNS Rhabdomyosarcoma_breaks.png (deflated 10%)
  adding: plots/tumor-type/Meningioma_breaks.png (deflated 17%)
  adding: plots/tumor-type/Ganglioglioma_breaks.png (deflated 16%)
  adding: plots/tumor-type/CNS sarcoma_breaks.png (deflated 13%)
  adding: plots/tumor-type/Germinoma_breaks.png (deflated 14%)
  adding: plots/tumor-type/Choroid plexus tumor_breaks.png (deflated 17%)
  adding: plots/tumor-type/CNS lymphoma_breaks.png (deflated 19%)
  adding: plots/tumor-type/Craniopharyngioma_breaks.png (deflated 17%)
  adding: plots/tumor-type/Teratoma_breaks.png (deflated 17%)
  adding: plots/tumor-type/CNS EFT-CIC_breaks.png (deflated 18%)
  adding: plots/tumor-type/Other_breaks.png (deflated 17%)
  adding: plots/tumor-type/Adenoma_breaks.png (deflated 15%)
  adding: plots/tumor-type/CNS ganglioneuroblastoma_breaks.png (deflated 15%)

Session Info

sessionInfo()
R version 3.6.0 (2019-04-26)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Debian GNU/Linux 9 (stretch)

Matrix products: default
BLAS/LAPACK: /usr/lib/libopenblasp-r0.2.19.so

locale:
 [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C               LC_TIME=en_US.UTF-8        LC_COLLATE=en_US.UTF-8    
 [5] LC_MONETARY=en_US.UTF-8    LC_MESSAGES=C              LC_PAPER=en_US.UTF-8       LC_NAME=C                 
 [9] LC_ADDRESS=C               LC_TELEPHONE=C             LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       

attached base packages:
[1] parallel  stats4    stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] GenomicRanges_1.38.0 GenomeInfoDb_1.22.0  IRanges_2.20.1       S4Vectors_0.24.1     BiocGenerics_0.32.0 

loaded via a namespace (and not attached):
  [1] colorspace_1.4-1            rjson_0.2.20                biovizBase_1.34.1          
  [4] circlize_0.4.8              htmlTable_1.13.1            XVector_0.26.0             
  [7] GlobalOptions_0.1.1         base64enc_0.1-3             dichromat_2.0-0            
 [10] clue_0.3-57                 rstudioapi_0.10             bit64_0.9-7                
 [13] AnnotationDbi_1.48.0        splines_3.6.0               R.methodsS3_1.7.1          
 [16] ggbio_1.34.0                knitr_1.23                  Formula_1.2-3              
 [19] jsonlite_1.6                Rsamtools_2.2.1             cluster_2.1.0              
 [22] dbplyr_1.4.2                png_0.1-7                   R.oo_1.22.0                
 [25] graph_1.62.0                pheatmap_1.0.12             BiocManager_1.30.4         
 [28] readr_1.3.1                 compiler_3.6.0              httr_1.4.0                 
 [31] backports_1.1.4             assertthat_0.2.1            Matrix_1.2-17              
 [34] lazyeval_0.2.2              acepack_1.4.1               htmltools_0.3.6            
 [37] prettyunits_1.0.2           tools_3.6.0                 gtable_0.3.0               
 [40] glue_1.3.1                  GenomeInfoDbData_1.2.2      reshape2_1.4.3             
 [43] dplyr_0.8.3                 rappdirs_0.3.1              Rcpp_1.0.1                 
 [46] Biobase_2.46.0              Biostrings_2.54.0           rtracklayer_1.46.0         
 [49] xfun_0.8                    stringr_1.4.0               ensembldb_2.10.2           
 [52] XML_3.98-1.20               zlibbioc_1.32.0             scales_1.0.0               
 [55] BSgenome_1.54.0             VariantAnnotation_1.32.0    ProtGenerics_1.18.0        
 [58] hms_0.4.2                   RBGL_1.62.1                 SummarizedExperiment_1.16.0
 [61] AnnotationFilter_1.10.0     RColorBrewer_1.1-2          ComplexHeatmap_2.2.0       
 [64] yaml_2.2.0                  curl_3.3                    memoise_1.1.0              
 [67] gridExtra_2.3               ggplot2_3.2.0               biomaRt_2.42.0             
 [70] rpart_4.1-15                reshape_0.8.8               latticeExtra_0.6-28        
 [73] stringi_1.4.3               RSQLite_2.1.1               checkmate_1.9.4            
 [76] GenomicFeatures_1.38.0      BiocParallel_1.20.0         shape_1.4.4                
 [79] rlang_0.4.0                 pkgconfig_2.0.2             matrixStats_0.55.0         
 [82] bitops_1.0-6                evaluate_0.14               lattice_0.20-38            
 [85] purrr_0.3.2                 GenomicAlignments_1.22.1    htmlwidgets_1.3            
 [88] labeling_0.3                cowplot_0.9.4               bit_1.1-14                 
 [91] tidyselect_0.2.5            GGally_1.4.0                plyr_1.8.4                 
 [94] magrittr_1.5                R6_2.4.0                    Hmisc_4.2-0                
 [97] DelayedArray_0.12.0         DBI_1.0.0                   pillar_1.4.2               
[100] foreign_0.8-71              survival_2.44-1.1           RCurl_1.95-4.12            
[103] nnet_7.3-12                 tibble_2.1.3                crayon_1.3.4               
[106] OrganismDbi_1.28.0          BiocFileCache_1.10.2        rmarkdown_1.13             
[109] GetoptLong_0.1.7            progress_1.2.2              grid_3.6.0                 
[112] data.table_1.12.2           blob_1.1.1                  digest_0.6.20              
[115] tidyr_0.8.3                 R.utils_2.9.0               openssl_1.4                
[118] munsell_0.5.0               askpass_1.1                
LS0tCnRpdGxlOiAiQ2hyb21vc29tYWwgSW5zdGFiaWxpdHk6IEJyZWFrcG9pbnRzIgpvdXRwdXQ6ICAgCiAgaHRtbF9ub3RlYm9vazogCiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQphdXRob3I6IENhbmRhY2UgU2F2b25lbiBmb3IgQUxTRiAtIENDREwKZGF0ZTogMjAyMAotLS0KClRoaXMgYW5hbHlzaXMgZXZhbHVhdGVzIGNocm9tb3NvbWFsIGluc3RhYmlsaXR5IGJ5IGZvcm1hdHRpbmcgU1YgYW5kIENOViBkYXRhIAppbnRvIGNocm9tb3NvbWFsIGJyZWFrcyBhbmQgbWVhc3VyaW5nIGNocm9tb3NvbWFsIGJyZWFrIGRlbnNpdHkuClRoaXMgbm90ZWJvb2sgcmV0dXJucyBjaHJvbW9zb21hbCBicmVhayBoZWF0bWFwcyBhbmQgcGxvdHMgZm9yIGVhY2ggdHVtb3IgdHlwZSAKZ3JvdXAgaW4gdGhlIGBwbG90c2AgZGlyZWN0b3J5LgoKQ29kZSBhZGFwdGVkIGZyb20gW3N2Y252cGx1c10oaHR0cHM6Ly9naXRodWIuY29tL2dvbnpvbGdhcmNpYS9zdmNudnBsdXMpLgoKIyMjIFVzYWdlCgpUaGlzIG5vdGVib29rIGNhbiBiZSBydW4gdmlhIHRoZSBjb21tYW5kIGxpbmUgZnJvbSB0aGUgdG9wIGRpcmVjdG9yeSBvZiB0aGUgCnJlcG9zaXRvcnkgYXMgZm9sbG93czoKCmBgYApSc2NyaXB0IC1lICJybWFya2Rvd246OnJlbmRlcignYW5hbHlzZXMvY2hyb21vc29tYWwtaW5zdGFiaWxpdHkvY2hyb21vc29tYWwtaW5zdGFiaWxpdHkuUm1kJywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsZWFuID0gVFJVRSkiCmBgYAoKIyMjIFNldCBVcAoKYGBge3J9CiMgU2V0IHNlZWQgc28gaGVhdG1hcHMgdHVybiBvdXQgdGhlIHNhbWUKc2V0LnNlZWQoMjAyMCkKCiMgVGhpcyB0aHJlc2hvbGQgd2lsbCBiZSB1c2VkIHRvIGRldGVybWluZSBwZXJjZW50IG9mIGdlbm9tZSBjaGFuZ2VkCmNoLnBjdCA8LSAwLjAyCgojIE1hZ3JpdHRyIHBpcGUKYCU+JWAgPC0gZHBseXI6OmAlPiVgCmBgYAoKUmVhZCBpbiB0aGUgY3VzdG9tIGZ1bmN0aW9ucyBuZWVkZWQgZm9yIHRoaXMgYW5hbHlzaXMuIAoKYGBge3J9CnNvdXJjZShmaWxlLnBhdGgoInV0aWwiLCAiY2hyLWJyZWFrLWNhbGN1bGF0ZS5SIikpCnNvdXJjZShmaWxlLnBhdGgoInV0aWwiLCAiY2hyLWJyZWFrLXBsb3QuUiIpKQpgYGAKCiMjIyBEaXJlY3RvcmllcyBhbmQgRmlsZXMKCmBgYHtyfQojIFBhdGggdG8gZGF0YSBkaXJlY3RvcnkKZGF0YV9kaXIgPC0gZmlsZS5wYXRoKCIuLiIsICIuLiIsICJkYXRhIikKCiMgUGF0aCB0byBvdXRwdXQgZGlyZWN0b3J5CnBsb3RzX2RpciA8LSAicGxvdHMiCgojIFBhdGggdG8gdHVtb3IgdHlwZSBwbG90cyBvdXRwdXQgZGlyZWN0b3J5Cmhpc3RfcGxvdHNfZGlyIDwtIGZpbGUucGF0aChwbG90c19kaXIsICJ0dW1vci10eXBlIikKCiMgQ3JlYXRlIHRoZSBoaXN0X3Bsb3RzX2RpciAgaWYgaXQgZG9lcyBub3QgZXhpc3QKaWYgKCFkaXIuZXhpc3RzKGhpc3RfcGxvdHNfZGlyKSkgewogIGRpci5jcmVhdGUoaGlzdF9wbG90c19kaXIsIHJlY3Vyc2l2ZSA9IFRSVUUpCn0KYGBgCgojIyMgUmVhZCBpbiBkYXRhIGFuZCBmb3JtYXQgaXQgCgojIyMjIFNldCB1cCBtZXRhZGF0YQoKYGBge3J9CiMgUmVhZCBpbiB0aGUgbWV0YWRhdGEKbWV0YWRhdGEgPC0gcmVhZHI6OnJlYWRfdHN2KGZpbGUucGF0aChkYXRhX2RpciwgInBidGEtaGlzdG9sb2dpZXMudHN2IikpCmBgYAoKIyMjIyBTZXQgdXAgQ05WIGRhdGE6IAoKUmVhZCBpbiB0aGUgQ05WIGRhdGEgYW5kIGZvcm1hdCBpdCB0byBtYWtlIHRoZSBjaHJvbW9zb21lIGFuZCBjb29yZGluYXRlcyBlYXNpZXIgZm9yCmhhbmRsaW5nIGxhdGVyLiAKCmBgYHtyfQojIFJlYWQgaW4gdGhlIHNlZ21lbnQgY29weSBudW1iZXIgZGF0YQpjbnZfZGYgPC0gZGF0YS50YWJsZTo6ZnJlYWQoZmlsZS5wYXRoKGRhdGFfZGlyLCAicGJ0YS1jbnYtY252a2l0LnNlZy5neiIpLAogIGRhdGEudGFibGUgPSBGQUxTRQopICU+JQogIGRwbHlyOjptdXRhdGUoCiAgICBzYW1wbGVzID0gYXMuZmFjdG9yKElEKSwKICAgICMgQ2FsY3VsYXRlIHdpZHRoCiAgICB3aWR0aCA9IGxvYy5lbmQgLSBsb2Muc3RhcnQsCiAgICAjIE1ha2UgYSBiaW5hcnkgdmFyaWFibGUgdGhhdCBsYWJlbHMgd2hldGhlciBvciBub3QgYSBzZXF1ZW5jZQogICAgIyBpcyBjb25zaWRlcmVkIGNoYW5nZWQgYnkgdGhlIHRocmVzaG9sZCBzZXQKICAgIGNoYW5nZWQgPSBhcy5mYWN0b3IoZHBseXI6OmNhc2Vfd2hlbigKICAgICAgc2VnLm1lYW4gPCBsb2cyKDEgLSBjaC5wY3QpIH4gImNoYW5nZSIsCiAgICAgIHNlZy5tZWFuID49IGxvZzIoMSArIGNoLnBjdCkgfiAiY2hhbmdlIiwgCiAgICAgIFRSVUUgfiAibm9fY2hhbmdlIgogICAgKSkKICApICU+JQogICMgUmVmb3JtYXQgdGhlIGNocm9tb3NvbWUgdmFyaWFibGUgdG8gZHJvcCB0aGUgImNociIKICBkcGx5cjo6bXV0YXRlKGNocm9tID0gZmFjdG9yKGdzdWIoImNociIsICIiLCBjaHJvbSksCiAgICBsZXZlbHMgPSBjKDE6MjIsICJYIiwgIlkiKQogICkpICU+JQogICMgQ2hhbmdpbmcgdGhlc2Ugc28gdGhleSBlbmQgdXAgbWF0Y2hpbmcgdGhlIFNWIGRhdGEKICBkcGx5cjo6cmVuYW1lKHN0YXJ0ID0gbG9jLnN0YXJ0LCBlbmQgPSBsb2MuZW5kKQpgYGAKCkZpbHRlciBDTlYgZGF0YSB0byBvbmx5IHRoZSBjaGFuZ2VzIHRoYXQgYXJlIGxhcmdlciB0aGFuIG91ciBjdXRvZmYgYGNoLnBjdGAuCgpgYGB7cn0KY252X2ZpbHRlcmVkX2RmIDwtIGNudl9kZiAlPiUKICBkcGx5cjo6ZmlsdGVyKGNoYW5nZWQgPT0gImNoYW5nZSIpCmBgYAoKIyMjIyBTZXQgdXAgU1YgZGF0YQoKUmVhZCBpbiB0aGUgU1YgZGF0YS4KCmBgYHtyfQpzdl9kZiA8LSBkYXRhLnRhYmxlOjpmcmVhZChmaWxlLnBhdGgoZGF0YV9kaXIsICJwYnRhLXN2LW1hbnRhLnRzdi5neiIpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YS50YWJsZSA9IEZBTFNFKSAlPiUKICAjIFJlZm9ybWF0IHRoZSAyMyBhbmQgMjQgY2hyb21vc29tZXMgc28gdGhleSBhcmUgWCBhbmQgWSBhbmQgYWxzbyBmYWN0b3JzCiAgZHBseXI6Om11dGF0ZSgKICAgIGNocm9tID0gZHBseXI6OnJlY29kZShTVi5jaHJvbSwKICAgICAgYDIzYCA9ICJYIiwgYDI0YCA9ICJZIgogICAgKQogICkKYGBgCgojIyMjIFNldCB1cCBjaHJvbW9zb21lIHJlZmVyZW5jZSBkYXRhCgpTZXQgdXAgY2hyb21vc29tZSBzaXplIGRhdGEuIApJdCBqdXN0IHNvIGhhcHBlbnMgdGhhdCB0aGlzIEJFRCBmaWxlOiBgV0dTLmhnMzguc3RyZWxrYTIudW5wYWRkZWQuYmVkYCBpcyBhY3R1YWxseSAKanVzdCBhIGxpc3Qgb2YgdGhlIGNocm9tb3NvbWUgc2l6ZXMgc28gd2UgYXJlIHVzaW5nIHRoYXQgZm9yIG5vdy4gCgpgYGB7cn0KIyBTZXQgdXAgQ2hyIHNpemVzCmNocl9zaXplcyA8LSByZWFkcjo6cmVhZF90c3YoZmlsZS5wYXRoKGRhdGFfZGlyLCAiV0dTLmhnMzguc3RyZWxrYTIudW5wYWRkZWQuYmVkIiksCiAgY29sX25hbWVzID0gRkFMU0UKKSAlPiUKICAjIFJlZm9ybWF0IHRoZSBjaHJvbW9zb21lIHZhcmlhYmxlIHRvIGRyb3AgdGhlICJjaHIiCiAgZHBseXI6Om11dGF0ZShYMSA9IGZhY3Rvcihnc3ViKCJjaHIiLCAiIiwgWDEpLAogICAgbGV2ZWxzID0gYygxOjIyLCAiWCIsICJZIiwgIk0iKQogICkpICU+JQogICMgUmVtb3ZlIHNleCBjaHJvbW9zb21lcwogIGRwbHlyOjpmaWx0ZXIoIShYMSAlaW4lIGMoIlgiLCAiWSIsICJNIikpKQoKIyBNYWtlIGNocm9tb3NvbWUgc2l6ZSBuYW1lZCB2ZWN0b3IKY2hyX3NpemVzX3ZlY3RvciA8LSBjaHJfc2l6ZXMkWDMKbmFtZXMoY2hyX3NpemVzX3ZlY3RvcikgPC0gY2hyX3NpemVzJFgxCmBgYAoKIyMgRm9ybWF0IHRoZSBkYXRhIGFzIGNocm9tb3NvbWVzIGJyZWFrcwoKVGhlIFNWIGFuZCBDTlYgZGF0YSBjb21lcyB0byB1cyBpbiB0aGUgZm9ybSBvZiByYW5nZXMsIGJ1dCBmb3IgZ2V0dGluZyBhIGxvb2sgCmF0IGNocm9tb3NvbWFsIGluc3RhYmlsaXR5LCB3ZSB3aWxsIHdhbnQgdG8gY29udmVydCB0aGlzIGRhdGEgaW50byBzaW5nbGUgYnJlYWsKcG9pbnRzIG9mIHRoZSBnZW5vbWUuIApXZSdsbCBhbHNvIHJlbW92ZSB0aGUgc2V4IGNocm9tb3NvbWVzLiAKCk1ha2UgYW4gQ05WIGJyZWFrcyBkYXRhLmZyYW1lLiAKCmBgYHtyfQojIE1ha2UgYSBDTlYgZGF0YS5mcmFtZSB0aGF0IGhhcyB0aGUgYnJlYWtzCmNudl9icmVha3MgPC0gZGF0YS5mcmFtZSgKICBzYW1wbGVzID0gcmVwKGNudl9maWx0ZXJlZF9kZiRJRCwgMiksCiAgY2hyb20gPSByZXAoY252X2ZpbHRlcmVkX2RmJGNocm9tLCAyKSwKICBzdGFydCA9IGMoY252X2ZpbHRlcmVkX2RmJHN0YXJ0LCBjbnZfZmlsdGVyZWRfZGYkZW5kKSwKICBlbmQgPSBjKGNudl9maWx0ZXJlZF9kZiRzdGFydCwgY252X2ZpbHRlcmVkX2RmJGVuZCksCiAgc2VnLm1lYW4gPSByZXAoY252X2ZpbHRlcmVkX2RmJHNlZy5tZWFuLCAyKSwKICBjb3B5Lm51bSA9IHJlcChjbnZfZmlsdGVyZWRfZGYkY29weS5udW0sIDIpLAogIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRQopICU+JQogICMgUmVtb3ZlIHNleCBjaHJvbW9zb21lcwogIGRwbHlyOjpmaWx0ZXIoIShjaHJvbSAlaW4lIGMoIlgiLCAiWSIpKSkKYGBgCgpNYWtlIGFuIFNWIGJyZWFrcyBkYXRhLmZyYW1lLiAKCmBgYHtyfQojIE1ha2UgYSBkYXRhLmZyYW1lIHRoYXQgaGFzIHRoZSBicmVha3MKc3ZfYnJlYWtzIDwtIGRhdGEuZnJhbWUoCiAgc2FtcGxlcyA9IHJlcChzdl9kZiRLaWRzLkZpcnN0LkJpb3NwZWNpbWVuLklELlR1bW9yLCAyKSwKICBjaHJvbSA9IHJlcChzdl9kZiRjaHJvbSwgMiksCiAgc3RhcnQgPSBjKHN2X2RmJFNWLnN0YXJ0LCBzdl9kZiRTVi5lbmQpLAogIGVuZCA9IGMoc3ZfZGYkU1Yuc3RhcnQsIHN2X2RmJFNWLmVuZCksCiAgc3ZjbGFzcyA9IHJlcChzdl9kZiRTVi50eXBlLCAyKSwKICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UKKSAlPiUKICAjIFJlbW92ZSBzZXggY2hyb21vc29tZXMgYW5kIE5BcwogIGRwbHlyOjpmaWx0ZXIoIShjaHJvbSAlaW4lIGMoIlgiLCAiWSIsICJNIiwgTkEpKSkKYGBgCgojIyMgQ28tbG9jYWxpemF0aW9uIG9mIGJyZWFrcG9pbnRzIGZvciBzYW1wbGVzIAoKRm9yIGVhY2ggc2FtcGxlLCB3ZSB3aWxsIGJpbiB0aGUgZ2Vub21lIGFuZCBjb3VudCBob3cgbWFueSBjaHJvbW9zb21lIGJyZWFrcyAKb2NjdXIgZm9yIGVhY2ggYmluLCBnaXZlbiB0aGUgZ2l2ZW4gYGJpbl9zaXplYC4KCldlIHdpbGwgcnVuIHRoaXMgZm9yIGVhY2ggc2FtcGxlIGFuZCByZXR1cm4gYSBsaXN0IG9mIHRocmVlIGBHZW5vbWljUmFuZ2VzYCBvYmplY3RzOgoxKSBgY29tbW9uX2RlbnNpdHlgIGNvbnRhaW5zIHRoZSBjb21iaW5lZCBicmVhayBjb3VudHMgZm9yIGJvdGggU1YgYW5kIENOViBicmVhayBkYXRhLiAgIAoyKSBgY252X2RlbnNpdHlgIGNvbnRhaW5zIHRoZSBudW1iZXIgb2YgYnJlYWsgY291bnRzIGZvciBDTlYuICAgCjMpIGBzdl9kZW5zaXR5YCBjb250YWlucyB0aGUgbnVtYmVyIG9mIGJyZWFrIGNvdW50cyBmb3IgU1YuICAKCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIHJlc3VsdHM9J2hpZGUnfQojIENoYW5nZSBoZXJlIGFuZCBpdCB3aWxsIGNoYW5nZSB0aGUgcmVzdApiaW5fc2l6ZSA8LSAxZTYKCiMgT2J0YWluIGEgbGlzdCBvZiBzYW1wbGVzIHRoYXQgYXJlIGNvbW1vbiB0byBib3RoIGRhdGFzZXRzLiAKY29tbW9uX3NhbXBsZXMgPC0gaW50ZXJzZWN0KHN2X2JyZWFrcyRzYW1wbGVzLCBjbnZfYnJlYWtzJHNhbXBsZXMpCgojIEdldCBhIGJpZyBsaXN0IG9mIGJyZWFrIGRlbnNpdGllcyBmb3IgZWFjaCBzYW1wbGUuCnNhbXBsZV9kZW5zaXRpZXMgPC0gbGFwcGx5KGNvbW1vbl9zYW1wbGVzLCBmdW5jdGlvbihzYW1wbGVfaWQpIHsKICBjaHJfYnJlYWtfbGlzdCgKICAgIGNudl9icmVha3MgPSBjbnZfYnJlYWtzLCAKICAgIHN2X2JyZWFrcyA9IHN2X2JyZWFrcywgCiAgICBzYW1wbGVfaWQgPSBzYW1wbGVfaWQsIAogICAgY2hyX3NpemVzX3ZlY3RvciA9IGNocl9zaXplc192ZWN0b3IpCn0pCgojIEJyaW5nIGFsb25nIHRoZSBzYW1wbGUgSURzCm5hbWVzKHNhbXBsZV9kZW5zaXRpZXMpIDwtIGNvbW1vbl9zYW1wbGVzCmBgYAoKIyMjIFNldCB1cCBmb3IgbWFraW5nIGhlYXRtYXBzIG9mIHRoZSBicmVha3BvaW50cwoKR2l2ZW4gdGhlIGBHZW5vbWljUmFuZ2VzYCBvYmplY3RzIGZvciBlYWNoIHNhbXBsZSwgY3JlYXRlIGEgY29tYmluZWQgcGxvdCBmb3IgCmVhY2guIAoKTWFrZSBjaHJvbW9zb21lIGxhYmVsaW5nIGBIZWF0bWFwQW5ub3RhdGlvbmAgb2JqZWN0LiAKCmBgYHtyfQojIEV4dHJhY3QgY2hyb21vc29tZSBuYW1lcyBmcm9tIGEgR2Vub21pY1JhbmdlcyBvYmplY3QKY2hyX2xhYmVscyA8LSBzYW1wbGVfZGVuc2l0aWVzW1sxXV0kY29tbW9uX2RlbnNpdHlAc2VxbmFtZXMKY2hyX2xhYmVscyA8LSBwYXN0ZTAoImNociIsIGFzLmZhY3RvcihjaHJfbGFiZWxzKSkKCiMgTWFrZSBhIGtleSBmb3IgYXNzaWduaW5nIGFsdGVybmF0aW5nIGNvbG9ycyB0byB0aGUgY2hyb21vc29tZXMKY2hyX2NvbG9ycyA8LSByZXAoYygiZ3JleSIsICJsaWdodGJsdWUiKSwgbGVuZ3RoLm91dCA9IGxlbmd0aCh1bmlxdWUoY2hyX2xhYmVscykpKQpuYW1lcyhjaHJfY29sb3JzKSA8LSB1bmlxdWUoY2hyX2xhYmVscykgCgojIENyZWF0ZSB0aGUgSGVhdG1hcCBhbm5vdGF0aW9uIG9iamVjdApjaHJfYW5ub3QgPC0gQ29tcGxleEhlYXRtYXA6OkhlYXRtYXBBbm5vdGF0aW9uKGRmID0gZGF0YS5mcmFtZShjaHJfbGFiZWxzKSwgY29sID0gbGlzdChjaHJfbGFiZWxzID0gYyhjaHJfY29sb3JzKSksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob3dfbGVnZW5kID0gRkFMU0UsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob3dfYW5ub3RhdGlvbl9uYW1lID0gRkFMU0UpCmBgYAoKTWFrZSBoaXN0b2xvZ3kgbGFiZWxpbmcgYEhlYXRtYXBBbm5vdGF0aW9uYCBvYmplY3QuCgpgYGB7cn0KIyBHZXQgdGhlIGhpc3RvbG9naWVzIGZvciB0aGUgc2FtcGxlcyBpbiB0aGlzIHNldCBhbmQgb3JkZXIgdGhlbSBieSBoaXN0b2xvZ3kKaGlzdG9sb2dpZXMgPC0gCiAgZGF0YS5mcmFtZShLaWRzX0ZpcnN0X0Jpb3NwZWNpbWVuX0lEID0gY29tbW9uX3NhbXBsZXMpICU+JQogIGRwbHlyOjppbm5lcl9qb2luKGRwbHlyOjpzZWxlY3QobWV0YWRhdGEsIEtpZHNfRmlyc3RfQmlvc3BlY2ltZW5fSUQsIHNob3J0X2hpc3RvbG9neSkpICU+JSAKICBkcGx5cjo6bXV0YXRlKHNob3J0X2hpc3RvbG9neSA9IGFzLmZhY3RvcihzaG9ydF9oaXN0b2xvZ3kpKSAlPiUgCiAgZHBseXI6OmFycmFuZ2Uoc2hvcnRfaGlzdG9sb2d5KSAlPiUgCiAgdGliYmxlOjpjb2x1bW5fdG9fcm93bmFtZXMoIktpZHNfRmlyc3RfQmlvc3BlY2ltZW5fSUQiKSAKCiMgQ3JlYXRlIHRoZSBIZWF0bWFwIGFubm90YXRpb24gb2JqZWN0Cmhpc3RfYW5ub3QgPC0gQ29tcGxleEhlYXRtYXA6OnJvd0Fubm90YXRpb24oZGYgPSBoaXN0b2xvZ2llcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaG93X2Fubm90YXRpb25fbmFtZSA9IEZBTFNFKQpgYGAKCk1ha2UgYSBjb2xvciBmdW5jdGlvbi4gCgpgYGB7cn0KY29sX2Z1biA8LSBjaXJjbGl6ZTo6Y29sb3JSYW1wMihjKDAsIDIsIDUsIDEwLCAyMCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYygiI2VkZjhmYiIsICIjYjJlMmUyIiwgIiM2NmMyYTQiLCAiIzJjYTI1ZiIsICIjMDA2ZDJjIikpCmBgYAoKTWFrZSBhIGZ1bmN0aW9uIGZvciBtYWtpbmcgdGhlIGhlYXRtYXBzLiAKCmBgYHtyfQpicmVha3NfaGVhdG1hcCA8LSBmdW5jdGlvbihkYXRhX25hbWUpIHsKICAjIEEgd3JhcHBlciBmdW5jdGlvbiBmb3IgbWFraW5nIGEgaGVhdG1hcCBmcm9tIHRoZSBzYW1wbGVzIEdlbm9taWNSYW5nZXMgbGlzdC4gCiAgIyAKICAjIEFyZ3M6CiAgIyBkYXRhX25hbWU6IGEgY2hhcmFjdGVyIHN0cmluZyB0aGF0IG1hdGNoZXMgYSBuYW1lIGluIHRoZSBsaXN0LiAKCiAgIyBSZXR1cm5zOiAKICAjIEEgaGVhdG1hcCBvZiB0aGUgY2hyb21vc29tYWwgYnJlYWtzCgogICMgUHVsbCBvdXQgdGhlIHRvdGFsX2NvdW50cyAKICB0b3RhbF9jb3VudHMgPC0gbGFwcGx5KHNhbXBsZV9kZW5zaXRpZXMsIGZ1bmN0aW9uKGdyYW5nZXNfbGlzdCkgewogICAgZ3Jhbmdlc19saXN0W1tkYXRhX25hbWVdXUBlbGVtZW50TWV0YWRhdGFAbGlzdERhdGEkdG90YWxfY291bnRzCiAgfSkKICAjIE1ha2UgaW50byBhIGRhdGEuZnJhbWUKICB0b3RhbF9jb3VudHMgPC0gZHBseXI6OmJpbmRfcm93cyh0b3RhbF9jb3VudHMpICU+JQogICAgZHBseXI6OnNlbGVjdChyb3duYW1lcyhoaXN0b2xvZ2llcykpICU+JSAKICAgIHQoKQogICMgQWRkIGNocm9tb3NvbWUgbGFiZWxzCiAgY29sbmFtZXModG90YWxfY291bnRzKSA8LSBjaHJfbGFiZWxzCiAgIyBQbG90IG9uIGEgaGVhdG1hcAogIGhlYXRtYXAgPC0gQ29tcGxleEhlYXRtYXA6OkhlYXRtYXAodG90YWxfY291bnRzLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2wgPSBjb2xfZnVuLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoZWF0bWFwX2xlZ2VuZF9wYXJhbSA9IGxpc3QodGl0bGUgPSAiQ291bnQgb2YgY2hyIGJyZWFrcyIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsdXN0ZXJfY29sdW1ucyA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsdXN0ZXJfcm93cyA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob3dfY29sdW1uX25hbWVzID0gRkFMU0UsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob3dfcm93X25hbWVzID0gRkFMU0UsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJvdHRvbV9hbm5vdGF0aW9uID0gY2hyX2Fubm90LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZWZ0X2Fubm90YXRpb24gPSBoaXN0X2Fubm90KQogICMgUmV0dXJuIHBsb3QKICByZXR1cm4oaGVhdG1hcCkKfQpgYGAKCiMjIENvbW1vbiBicmVha3MgaGVhdG1hcAoKYGBge3J9CmNvbW1vbl9oZWF0bWFwIDwtIGJyZWFrc19oZWF0bWFwKGRhdGFfbmFtZSA9ICJjb21tb25fZGVuc2l0eSIpCgojIFNhdmUgcGxvdCBhcyBQTkcKcG5nKGZpbGUucGF0aChwbG90c19kaXIsIHBhc3RlMCgiY29tbW9uX2RlbnNpdHlfaGVhdG1hcC5wbmciKSksIAogIHdpZHRoID0gMTIwMCwgaGVpZ2h0ID0gOTAwLCB1bml0cyA9ICJweCIpCiAgY29tbW9uX2hlYXRtYXAKZGV2Lm9mZigpCgojIFByaW50IG91dCBoZXJlCmNvbW1vbl9oZWF0bWFwCmBgYAoKIyMgQ05WIGJyZWFrcyBoZWF0bWFwCgpgYGB7cn0KY252X2hlYXRtYXAgPC0gYnJlYWtzX2hlYXRtYXAoZGF0YV9uYW1lID0gImNudl9kZW5zaXR5IikKCiMgU2F2ZSBwbG90IGFzIFBORwpwbmcoZmlsZS5wYXRoKHBsb3RzX2RpciwgcGFzdGUwKCJjbnZfZGVuc2l0eV9oZWF0bWFwLnBuZyIpKSwgCiAgd2lkdGggPSAxMjAwLCBoZWlnaHQgPSA5MDAsIHVuaXRzID0gInB4IikKICBjbnZfaGVhdG1hcApkZXYub2ZmKCkKCiMgUHJpbnQgb3V0IGhlcmUKY252X2hlYXRtYXAKYGBgCgojIyBTViBicmVha3MgaGVhdG1hcAoKYGBge3J9CnN2X2hlYXRtYXAgPC0gYnJlYWtzX2hlYXRtYXAoZGF0YV9uYW1lID0gInN2X2RlbnNpdHkiKQoKIyBTYXZlIHBsb3QgYXMgUE5HCnBuZyhmaWxlLnBhdGgocGxvdHNfZGlyLCBwYXN0ZTAoInN2X2RlbnNpdHlfaGVhdG1hcC5wbmciKSksIAogIHdpZHRoID0gMTIwMCwgaGVpZ2h0ID0gOTAwLCB1bml0cyA9ICJweCIpCiAgc3ZfaGVhdG1hcApkZXYub2ZmKCkKCiMgUHJpbnQgb3V0IGhlcmUKc3ZfaGVhdG1hcApgYGAKCiMjIyBDby1sb2NhbGl6YXRpb24gb2YgYnJlYWtwb2ludHMgZm9yIHR1bW9yLXR5cGUgZ3JvdXBzCgpTYW1lIGFzIHdhcyBkb25lIGZvciBlYWNoIHNhbXBsZSwgbm93IHdlIHdpbGwgY2FsY3VsYXRlIGRlbnNpdGllcyBmb3IgCmVhY2ggdHVtb3ItdHlwZSBncm91cC4gCgpgYGB7ciwgcmVzdWx0cz0naGlkZSd9CiMgQ2hhbmdlIGJpbl9zaXplIGhlcmUgYW5kIGl0IHdpbGwgY2hhbmdlIHRoZSByZXN0CmJpbl9zaXplIDwtIDFlNgoKIyBHZXQgYSBsaXN0IG9mIHRoZSB0dW1vcl90eXBlcyBmb3Igd2hpY2ggd2UgaGF2ZSBETkEtc2VxIGRhdGEKdHVtb3JfdHlwZXMgPC0gbWV0YWRhdGEgJT4lIAogIGRwbHlyOjpmaWx0ZXIoIWlzLm5hKHNob3J0X2hpc3RvbG9neSksIGV4cGVyaW1lbnRhbF9zdHJhdGVneSAhPSAiUk5BLVNlcSIpICU+JSAKICBkcGx5cjo6ZGlzdGluY3Qoc2hvcnRfaGlzdG9sb2d5KSAlPiUKICBkcGx5cjo6cHVsbChzaG9ydF9oaXN0b2xvZ3kpCmBgYAoKUnVuIHRoZSBkZW5zaXR5IGNhbGN1bGF0aW9ucyBmb3IgdGhlIGdyb3Vwcy4gCgpgYGB7cn0KIyBHZXQgYSBiaWcgbGlzdCBvZiBicmVhayBkZW5zaXRpZXMgZm9yIGVhY2ggc2FtcGxlLgpncm91cF9kZW5zaXRpZXMgPC0gbGFwcGx5KHR1bW9yX3R5cGVzLCBmdW5jdGlvbih0dW1vcl90eXBlKSB7CgogICMgT2J0YWluIGEgbGlzdCBvZiBzYW1wbGVfaWRzIHRvIHN1YnNldCBieQogIHNhbXBsZV9pZHMgPC0gbWV0YWRhdGEgJT4lCiAgICBkcGx5cjo6ZmlsdGVyKG1ldGFkYXRhJHNob3J0X2hpc3RvbG9neSA9PSB0dW1vcl90eXBlKSAlPiUKICAgIGRwbHlyOjpwdWxsKEtpZHNfRmlyc3RfQmlvc3BlY2ltZW5fSUQpCiAgCiAgIyBEb3VibGUgY2hlY2sgdGhlc2Ugc2FtcGxlcyBhcmUgaW4gdGhlIGxpc3QKICBjaGVja19zYW1wbGVzIDwtIChzYW1wbGVfaWRzICVpbiUgY29tbW9uX3NhbXBsZXMpCiAgCiAgIyBJZiBubyBzYW1wbGVzLCBnbyB0byBuZXh0CiAgaWYgKHN1bShjaGVja19zYW1wbGVzKSA+IDEpIHsKICBtZXNzYWdlKHBhc3RlKCJDYWxjdWxhdGluZyBicmVha3BvaW50IGRlbnNpdHkgZm9yIiwgdHVtb3JfdHlwZSwgInNhbXBsZXMiKSkKICBjaHJfYnJlYWtfbGlzdChjbnZfYnJlYWtzID0gY252X2JyZWFrcywgCiAgICAgICAgICAgICAgICAgc3ZfYnJlYWtzID0gc3ZfYnJlYWtzLCAKICAgICAgICAgICAgICAgICBzYW1wbGVfaWQgPSBzYW1wbGVfaWRzLCAKICAgICAgICAgICAgICAgICBjaHJfc2l6ZXNfdmVjdG9yID0gY2hyX3NpemVzX3ZlY3RvcikKICB9Cn0pCgojIEJyaW5nIGFsb25nIHRoZSB0dW1vci10eXBlIGxhYmVscwpuYW1lcyhncm91cF9kZW5zaXRpZXMpIDwtIHR1bW9yX3R5cGVzCgojIFJlbW92ZSB0dW1vcl90eXBlcyBkYXRhIHRoYXQgZGlkbid0IGhhdmUgYXQgbGVhc3QgdHdvIHNhbXBsZXMKZ3JvdXBfZGVuc2l0aWVzIDwtIGdyb3VwX2RlbnNpdGllc1shc2FwcGx5KGdyb3VwX2RlbnNpdGllcywgaXMubnVsbCldCmBgYAoKIyMjIFBsb3QgdGhlIGJyZWFrcG9pbnRzIGZvciBlYWNoIHR1bW9yLXR5cGUKCkhlcmUgd2Ugd2lsbCBwbG90IG1lZGlhbiBudW1iZXIgb2YgYnJlYWsgcG9pbnRzIGZvciB0aGUgdHVtb3ItdHlwZSBncm91cCBwZXIgCmVhY2ggYmluLgoKYGBge3J9CnB1cnJyOjppbWFwKGdyb3VwX2RlbnNpdGllcywgZnVuY3Rpb24oLngsIG5hbWUgPSAueSl7CiAgIyBNYWtlIHRoZSBjb21ibyBwbG90CiAgbXVsdGlwYW5lbF9icmVha19wbG90KAogICAgZ3Jhbmdlc19saXN0ID0gLngsCiAgICBwbG90X25hbWUgPSBuYW1lLAogICAgeV92YWwgPSAibWVkaWFuX2NvdW50cyIsCiAgICB5X2xhYiA9ICJNZWRpYW4gQnJlYWtzIHBlciBNYiIsCiAgICBwbG90X2RpciA9IGhpc3RfcGxvdHNfZGlyCiAgKQp9KQpgYGAKClppcCB1cCB0aGUgUE5HIGZpbGVzIGludG8gb25lIGZpbGUuIAoKYGBge3J9CiMgRGVjbGFyZSBuYW1lIG9mIHppcCBmaWxlCnppcF9maWxlIDwtIHBhc3RlMChoaXN0X3Bsb3RzX2RpciwgIi56aXAiKQoKIyBSZW1vdmUgYW55IGN1cnJlbnQgemlwX2ZpbGUgb2YgdGhpcyBuYW1lIHNvIHdlIGNhbiBvdmVyd3JpdGUgaXQKaWYgKGZpbGUuZXhpc3RzKHppcF9maWxlKSl7CiAgZmlsZS5yZW1vdmUoemlwX2ZpbGUpCn0KIyBaaXAgdXAgdGhlIHBsb3RzCnppcCh6aXBfZmlsZSwgaGlzdF9wbG90c19kaXIpCmBgYAoKIyMjIFNlc3Npb24gSW5mbwoKYGBge3J9CnNlc3Npb25JbmZvKCkKYGBgCgo=